/***************************************************************************
 *   Copyleft 2006-2007 by												   *
 *	 Iwanicki Krzysztof													   *
 *   <gamma.mini.pw.edu.pl/~iwanickik>			                           *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 ***************************************************************************/
#include "./AbstractWindow.h"
#include <commctrl.h>
#include "../resource.h"

#include "../MemTracer/MemoryTracker.h"
AbstractWindow::AbstractWindow(HINSTANCE mhInstance, const pcTChar name, const pcTChar title, HWND parent) {
	this->mhInstance = cNull;
	this->mhWnd = cNull;
	mStatusBar = cNull;
	mEngine = new Engine3D();
	Register(mhInstance, name);
	Create(name, title, parent);
	::InitCommonControls();
}

AbstractWindow::~AbstractWindow() {
	if (mStatusBar) {
		delete mStatusBar;
	}
}

cVoid AbstractWindow::SetProperty(cInt property, cLong value) {
	if (property == GWL_STYLE || property == GWL_EXSTYLE) {
		::SetWindowLong(this->mhWnd, property, value);
	} else {
		::SetClassLong(this->mhWnd, property, value);
	}
}

cVoid AbstractWindow::SetSize(cInt width, cInt height) {
	RECT rect, cl;

	::GetWindowRect(this->mhWnd, &rect);
	::GetClientRect(this->mhWnd, &cl);
	width += rect.right - rect.left - cl.right;
	height += rect.bottom - rect.top - cl.bottom;
	cInt xPos = (::GetSystemMetrics(SM_CXSCREEN) >> 1) - (width >> 1);
	cInt yPos = (::GetSystemMetrics(SM_CYSCREEN) >> 1) - (height >> 1);
	::MoveWindow(this->mhWnd, xPos, yPos, width, height, true);
}

cVoid AbstractWindow::SetLocation(cInt x, cInt y, cBool bCenter) {
	if (bCenter) {
		x = (::GetSystemMetrics(SM_CXSCREEN) >> 1) - (mEngine->GetWidth() >> 1);
		y = (::GetSystemMetrics(SM_CYSCREEN) >> 1) - (mEngine->GetHeight() >> 1);
	}
	::SetWindowPos(this->mhWnd, HWND_TOP, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
}

ATOM AbstractWindow::Register(HINSTANCE mhInstance, const pcTChar name) {
	WNDCLASSEX wcex;

	this->mhInstance = mhInstance;

	wcex.cbSize			= sizeof(WNDCLASSEX);
	wcex.style			= CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
	wcex.lpfnWndProc	= (WNDPROC) MainWndProc;
	wcex.cbClsExtra		= 0;
	wcex.cbWndExtra		= 0;
	wcex.hInstance		= mhInstance;
	wcex.hIcon			= ::LoadIcon(mhInstance, MAKEINTRESOURCE(IDI_VTEAMICONMEDIUM));
	wcex.hCursor		= cNull;
	wcex.hbrBackground	= (HBRUSH) ::GetStockObject(WHITE_BRUSH);
	wcex.lpszMenuName	= cNull;
	wcex.lpszClassName	= name;
	wcex.hIconSm		= ::LoadIcon(mhInstance, MAKEINTRESOURCE(IDI_VTEAMICON));

	return ::RegisterClassEx(&wcex);
}

ATOM AbstractWindow::Create(const pcTChar name, const pcTChar title, HWND parent) {
	this->mhWnd = ::CreateWindowEx(
				0,			    // window extended style
				name,			    // window class name
				title,			    // window caption
				WS_OVERLAPPEDWINDOW,        // window style
				CW_USEDEFAULT,              // initial x position
				CW_USEDEFAULT,              // initial y position
				CW_USEDEFAULT,              // initial x size
				CW_USEDEFAULT,              // initial y size
				cNull,			    // parent window handle
				cNull,                       // window menu handle
				this->mhInstance,		    // program instance handle
				(cVoid *) this);             // creation parameters
    if (!this->mhWnd) {
      return false;
    }

	SetSize(WINDOW_WIDTH, WINDOW_HEIGHT);
	mEngine->Init(this->mhWnd);
	
	::ShowWindow(this->mhWnd, SW_SHOW);
	::UpdateWindow(this->mhWnd);

	return true;
}

LRESULT CALLBACK AbstractWindow::MainWndProc(HWND hwnd, cUInt message, WPARAM wParam, LPARAM lParam) {
	PAINTSTRUCT ps;
	HDC hdc;
	RECT updateRect, clientRect;
	AbstractWindow *pAbstractWindow;

	if (message == WM_NCCREATE) {
		::SetWindowLong(hwnd, GWL_USERDATA, (cLong) (LONG_PTR) ((LPCREATESTRUCT (lParam))->lpCreateParams));
	}
	pAbstractWindow = (AbstractWindow *)(LONG_PTR) ::GetWindowLong(hwnd, GWL_USERDATA);

	if (pAbstractWindow) {
		switch (message) {
			case WM_CREATE:
				::InitCommonControls();
				::SetTimer(hwnd, 1, 1, cNull);
				pAbstractWindow->mEngine->Refresh();
				break;
			case WM_TIMER:
				pAbstractWindow->mEngine->Refresh();
				break;
			case WM_ERASEBKGND:
				return true;
			case WM_PAINT:
				if (pAbstractWindow->mEngine) {
					::GetUpdateRect(hwnd, &updateRect, false);
					::GetClientRect(hwnd, &clientRect);
					hdc = ::BeginPaint(hwnd, &ps);
					if (updateRect.left == updateRect.right &&
						updateRect.top == updateRect.bottom) {
						pAbstractWindow->mEngine->Render(hdc, clientRect);
					} else {
						pAbstractWindow->mEngine->Render(hdc, updateRect);
					}
					::EndPaint(hwnd, &ps);
				}
				return TRUE;
			case WM_CLOSE:
				::PostQuitMessage(0);
				break;
			case WM_DESTROY:
				::KillTimer(pAbstractWindow->mhWnd, 1);
				delete pAbstractWindow->mEngine;	
				break;
		}
		return pAbstractWindow->WndProc(hwnd, message, wParam, lParam);
	} else {
        return ::DefWindowProc(hwnd, message, wParam, lParam);
	}
}

/*virtual*/ LRESULT CALLBACK AbstractWindow::WndProc(HWND hwnd, cUInt message, WPARAM wParam, LPARAM lParam) {
	return ::DefWindowProc(hwnd, message, wParam, lParam);
}